package com.onpositive.simple.persistance;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.EmptyStackException;
import java.util.HashMap;
import java.util.Stack;

public class FSNodeStorage extends AbstractNodeStorage {
	
	final static int intSize = 4  ;
	
	protected Stack<Integer> deletedNodes ;
	protected final int containerOffset = 1*intSize ;
	
	protected int nodeSize, nodesCount ;
	
	protected RandomAccessFile container ;
	protected HashMap<Integer,FSTreeNode> cash ;
	ByteBuffer byteBuffer ;
	IntBuffer intBuffer ;// = byteBuffer.asIntBuffer() ;	
	
	public FSNodeStorage( int nodeSize,  String path, String postfix   ){
		
		this.nodeSize = nodeSize ;
		nodesCount = 0 ;
		deletedNodes = new Stack<Integer>() ;
		cash = new HashMap<Integer, FSTreeNode>();
		
		String containerPath = path + "ncontainer" + postfix + ".dat" ;
		
		try {
			container = new RandomAccessFile(containerPath, "rws") ;
			container.setLength(0);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		byteBuffer = ByteBuffer.allocate(intSize*nodeSize) ;
		intBuffer = byteBuffer.asIntBuffer() ;
	}

	@Override
	public AbstractTreeNode getNode( int nodeIndex ) {
		
		//TODO look into the cash first
		
		byte content[] = new byte[ intSize*nodeSize ] ;
		
		try {
			container.seek(containerOffset + intSize*nodeSize*nodeIndex) ;
			container.read(content, 0, intSize*nodeSize ) ;
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		byteBuffer.clear() ;
		intBuffer.clear() ;
		byteBuffer.put( content ) ;
		int nodeContainer[] = new int[ nodeSize ] ;
		intBuffer.get(nodeContainer) ;		
		
		return new FSTreeNode( this, nodeIndex, nodeSize, nodeContainer ) ;
	}
	@Override
	public AbstractTreeNode getNewNode() {
		
		int nodeIndex = getVacantNode() ;
		nodesCount++ ;
		updateNodesCount() ;
		
		intBuffer.clear() ;
		byteBuffer.clear();
		
		intBuffer.put( nodesCount ) ;
		byte content[] = new byte[ intSize ] ;
		byteBuffer.get(content) ;
		
		try {
			container.seek(0) ;
			container.write(content, 0, intSize) ;
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		try {
			if( container.length() < containerOffset + nodeSize*intSize*nodesCount )
				reallocateContainer(containerOffset + nodeSize*intSize*nodesCount) ;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		FSTreeNode treeNode = new FSTreeNode(this, nodeIndex, nodeSize) ;
		
		for( int i = masterIndex.zeroSubtreeOffset ; i < masterIndex.zeroSubtreeOffset + masterIndex.nodeMaxOrder+1 ; i++ )
			treeNode.setElement(i, masterIndex.emptySubtreeIndicator ) ;
		
		treeNode.setElement( masterIndex.parentNodeOffset, masterIndex.emptySubtreeIndicator) ;
		
		return treeNode ;
	}


	private void updateNodesCount() {
		byteBuffer.clear() ;
		intBuffer.clear() ;
		intBuffer = byteBuffer.asIntBuffer() ;
		intBuffer.put(nodesCount) ;
		
		byte initialParams[] = new byte[intSize] ; 
		byteBuffer.get(initialParams,0,intSize) ;
		try {
			container.seek(0) ;
			container.write( initialParams, 0, intSize) ;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

	@Override
	public void putNode(AbstractTreeNode node, int nodeIndex) {
		
		intBuffer.clear() ;
		byteBuffer.clear();
		
		intBuffer.put( ((FSTreeNode)node).localContainer ) ;
		byte content[] = new byte[ intSize*nodeSize ] ;
		byteBuffer.get(content) ;

		try {
			container.seek(containerOffset + intSize*nodeSize*nodeIndex) ;
			container.write(content, 0, nodeSize*intSize ) ;
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private int getVacantNode(){
		
		Integer vacantNode ;
		try{
			vacantNode = deletedNodes.pop() ;
		}
		catch(EmptyStackException e){
			
			vacantNode = nodesCount ;
		}
		return vacantNode ;
	}
	
	private void reallocateContainer(int requiredLength) {


		long length ;
		try {
			length = (int) container.length() ;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			length = 1 ;
		}
		
		for (  ; length < requiredLength ; )
			length = (long)(length * 1.4 + 1) ;
		
		try {
			container.setLength(length) ;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		
	}
	
}
